package com.vca.front.service.Impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vca.common.constants.Constants;
import com.vca.common.constants.PayConstants;
import com.vca.common.constants.SysGroupDataConstants;
import com.vca.common.constants.WeChatConstants;
import com.vca.common.exception.ExceptionCodeEnum;
import com.vca.common.exception.VcaException;
import com.vca.common.model.finance.UserRecharge;
import com.vca.common.model.user.User;
import com.vca.common.model.user.UserToken;
import com.vca.common.request.PhoneDecryptionRequest;
import com.vca.common.request.RegisterThirdUserRequest;
import com.vca.common.request.UserRechargeRequest;
import com.vca.common.request.WxBindingPhoneVoRequest;
import com.vca.common.response.LoginResponse;
import com.vca.common.response.OrderPayResultResponse;
import com.vca.common.response.UserRechargeFrontResponse;
import com.vca.common.response.UserRechargeItemResponse;
import com.vca.common.token.FrontTokenComponent;
import com.vca.common.utils.*;
import com.vca.common.vo.MyRecord;
import com.vca.common.vo.SystemGroupDataRechargeConfigVo;
import com.vca.common.vo.WeChatMiniAuthorizeVo;
import com.vca.common.vo.WxPayJsResultVo;
import com.vca.front.service.UserCenterService;
import com.vca.service.dao.user.UserDao;
import com.vca.service.service.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author chenbing
 * @date 2022/10/8   15:35
 */
@Service
public class UserCenterServiceImpl extends ServiceImpl<UserDao, User> implements UserCenterService {
    private static String TAG = "UserCenterServiceImpl";

    @Autowired
    private UserService userService;

    @Autowired
    private UserRechargeService userRechargeService;

    @Autowired
    private WeChatPayService weChatPayService;

    @Autowired
    private WechatNewService wechatNewService;

    @Autowired
    private UserTokenService userTokenService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private SystemGroupDataService systemGroupDataService;

    @Autowired
    private FrontTokenComponent tokenComponent;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * @param code    String 前端临时授权code
     * @param request RegisterThirdUserRequest 用户信息
     * @Description:微信登录小程序授权登录
     * @Author: chenBing
     * @Date: 2022/10/8
     */
    @Override
    public LoginResponse weChatAuthorizeProgramLogin(String code, RegisterThirdUserRequest request) {
        WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(code);
        LoginResponse loginResponse = new LoginResponse();
        System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
        // TODO: 2022/9/28 判断openID所对应的register_key是否存在
        Object registerKey = redisUtil.hmGet("register_key", response.getOpenId());
        Log.info(TAG, "registerKey=====>{ " + registerKey + " }");
        if (ObjectUtil.isNotEmpty(registerKey)) {
            //todo registerKey存在，直接跳转绑定手机号
            loginResponse.setKey(String.valueOf(registerKey));
            // TODO: 2022/10/8 用户已经被记录过用户信息（即已经授权过用户信息），直接走手机号绑定那一步  ready
            loginResponse.setType(3);
            return loginResponse;
        }
        //检测是否存在
        UserToken userToken = userTokenService.getByOpenidAndType(response.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM);
        if (ObjectUtil.isNotNull(userToken)) {// 已存在，正常登录
            User user = userService.getById(userToken.getUid());
            if (!user.getStatus()) {
                throw new VcaException("当前账户已禁用，请联系管理员！");
            }
            // 记录最后一次登录时间
            user.setLastLoginTime(DateUtil.nowDateTime());
            Boolean execute = transactionTemplate.execute(e -> {
                userService.updateById(user);
                return Boolean.TRUE;
            });
            if (!execute) {
                Log.error(TAG, StrUtil.format("小程序登录修改用户信息失败，uid={}", user.getUid()));
            }

            try {
                String token = tokenComponent.createToken(user);
                loginResponse.setToken(token);
            } catch (Exception e) {
                Log.error(TAG, StrUtil.format("小程序登录生成token失败，uid={}", user.getUid()));
                e.printStackTrace();
            }
            // TODO: 2022/10/8  用户已注册，进行登录 login
            loginResponse.setType(4);
            loginResponse.setUid(user.getUid());
            loginResponse.setOpenID(response.getOpenId());
            loginResponse.setUnionID(response.getUnionId());
//            loginResponse.setNickName(EmojiUtils.emojiConverter2Unicode(user.getNickname()));
            loginResponse.setNickName(user.getNickname());
            loginResponse.setPhone(user.getPhone());
            return loginResponse;
        }
        if (StrUtil.isBlank(request.getNickName())) {
            // TODO: 2022/10/8 用户未注册，走起始页进行注册登录  start
            loginResponse.setType(1);
            loginResponse.setOpenID(response.getOpenId());
            return loginResponse;
        }
        request.setType(Constants.USER_LOGIN_TYPE_PROGRAM);
        request.setOpenId(response.getOpenId());
        request.setUnionId(response.getUnionId());
        String key = MiracleAECUtil.encrypt(response.getOpenId());
        redisUtil.set(key, JSONObject.toJSONString(request));
        redisUtil.hmSet("register_key", response.getOpenId(), key);
        // TODO: 2022/10/8 用户第一次进行授权用户信息，进行注册登录 register
        loginResponse.setType(2);
        loginResponse.setKey(key);
        return loginResponse;
    }

    /**
     * @param request 请求参数
     * @Description:微信注册绑定手机号
     * @Author: chenBing
     * @Date: 2022/10/8
     */
    @Override
    public LoginResponse registerBindingPhone(WxBindingPhoneVoRequest request) {
        checkBindingPhone(request);
        // 进入创建用户绑定手机号流程
        Object o = redisUtil.get(request.getKey());
        if (ObjectUtil.isNull(o)) {
//            throw new VcaException("用户缓存已过期，请清除缓存重新登录");
            throw new VcaException(ExceptionCodeEnum.CACHEHASEXPIRED.getMessage());
        }
        RegisterThirdUserRequest registerThirdUserRequest = JSONObject.parseObject(o.toString(), RegisterThirdUserRequest.class);

        boolean isNew = true;

        User user = userService.getByPhone(request.getPhone());
        if (ObjectUtil.isNull(user)) {
            user = userService.registerByThird(registerThirdUserRequest);
            user.setPhone(request.getPhone());
            user.setAccount(request.getPhone());
            user.setPwd(CommonUtil.createPwd(request.getPhone()));
        } else {// 已有账户，关联到之前得账户即可
            if (user.getUserRegisterType()==2){
                throw new VcaException("手机号已被该系统内部员工注册，请重新输入手机号进行注册");
            }
            // 查询是否用对应得token
            int type = 0;
            switch (request.getType()) {
                case "public":
                    type = Constants.THIRD_LOGIN_TOKEN_TYPE_PUBLIC;
                    break;
                case "routine":
                    type = Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM;
                    break;
                default:
                    break;
            }

            UserToken userToken = userTokenService.getTokenByUserId(user.getUid(), type);
            if (ObjectUtil.isNotNull(userToken)) {
                throw new VcaException("该手机号已被注册");
            }
            isNew = false;
        }
        User finalUser = user;
//        finalUser.setNickname(EmojiUtils.emojiConverter2Html(user.getNickname()));
        finalUser.setNickname(user.getNickname());
        finalUser.setUnionId(registerThirdUserRequest.getUnionId());
        boolean finalIsNew = isNew;
        Boolean execute = transactionTemplate.execute(e -> {
            if (finalIsNew) {// 新用户
                userService.save(finalUser);
            }
            switch (request.getType()) {
                case "public":
                    userTokenService.bind(registerThirdUserRequest.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PUBLIC, finalUser.getUid());
                    break;
                case "routine":
                    userTokenService.bind(registerThirdUserRequest.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM, finalUser.getUid());
                    break;
                default:
                    break;
            }
            return Boolean.TRUE;
        });
        if (!execute) {
            Log.error(TAG, "微信用户注册生成失败，nickName = " + registerThirdUserRequest.getNickName());
        }
        LoginResponse loginResponse = new LoginResponse();
        try {
            String token = tokenComponent.createToken(finalUser);
            loginResponse.setToken(token);
        } catch (Exception e) {
            Log.error(TAG, StrUtil.format("绑定手机号，自动登录生成token失败，uid={}", finalUser.getUid()));
            e.printStackTrace();
        }
        if (redisUtil.hashExists("register_key", MiracleAECUtil.decrypt(request.getKey()))) {
            redisUtil.hmDelete("register_key", MiracleAECUtil.decrypt(request.getKey()));
        }
        loginResponse.setType(4);
        loginResponse.setUid(user.getUid());
//        loginResponse.setNickName(EmojiUtils.emojiConverter2Unicode(user.getNickname()));
        loginResponse.setNickName(user.getNickname());
        loginResponse.setPhone(user.getPhone());
        loginResponse.setOpenID(registerThirdUserRequest.getOpenId());
        loginResponse.setUnionID(registerThirdUserRequest.getUnionId());
        return loginResponse;
    }

    /**
     * @Description:绑定手机号数据校验
     * @Author: chenBing
     * @Date: 2022/10/8
     */
    private void checkBindingPhone(WxBindingPhoneVoRequest request) {
        if (!request.getType().equals("public") && !request.getType().equals("routine") && !request.getType().equals("iosWx") && !request.getType().equals("androidWx")) {
            throw new VcaException("未知的用户类型");
        }
        if (request.getType().equals("routine")) {
            // 参数校验
            if (StrUtil.isBlank(request.getCode())) {
                throw new VcaException("小程序获取手机号code不能为空");
            }
            if (StrUtil.isBlank(request.getEncryptedData())) {
                throw new VcaException("小程序获取手机号加密数据不能为空");
            }
            if (StrUtil.isBlank(request.getIv())) {
                throw new VcaException("小程序获取手机号加密算法的初始向量不能为空");
            }
            // 获取appid
            String programAppId = systemConfigService.getValueByKey(WeChatConstants.WECHAT_MINI_APPID);
            if (StringUtils.isBlank(programAppId)) {
                throw new VcaException("微信小程序appId未设置");
            }

            WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(request.getCode());
            System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
            String decrypt = WxUtil.decrypt(programAppId, request.getEncryptedData(), response.getSessionKey(), request.getIv());
            if (StrUtil.isBlank(decrypt)) {
                throw new VcaException("微信小程序获取手机号解密失败");
            }
            JSONObject jsonObject = JSONObject.parseObject(decrypt);
            if (StrUtil.isBlank(jsonObject.getString("phoneNumber"))) {
                throw new VcaException("微信小程序获取手机号没有有效的手机号");
            }
            request.setPhone(jsonObject.getString("phoneNumber"));
        }
    }

    /**
     * @Description:手机号解密
     * @author:chenbing
     * @date 2022/12/20 13:12
     */
    @Override
    public String phoneDecryption(PhoneDecryptionRequest request) {
        // 参数校验
        if (StrUtil.isBlank(request.getCode())) {
            throw new VcaException("小程序获取手机号code不能为空");
        }
        if (StrUtil.isBlank(request.getEncryptedData())) {
            throw new VcaException("小程序获取手机号加密数据不能为空");
        }
        if (StrUtil.isBlank(request.getIv())) {
            throw new VcaException("小程序获取手机号加密算法的初始向量不能为空");
        }
        // 获取appid
        String programAppId = systemConfigService.getValueByKey(WeChatConstants.WECHAT_MINI_APPID);
        if (StringUtils.isBlank(programAppId)) {
            throw new VcaException("微信小程序appId未设置");
        }

        WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(request.getCode());
        System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
        String decrypt = WxUtil.decrypt(programAppId, request.getEncryptedData(), response.getSessionKey(), request.getIv());
        if (StrUtil.isBlank(decrypt)) {
            throw new VcaException("微信小程序获取手机号解密失败");
        }
        JSONObject jsonObject = JSONObject.parseObject(decrypt);
        if (StrUtil.isBlank(jsonObject.getString("phoneNumber"))) {
            throw new VcaException("微信小程序获取手机号没有有效的手机号");
        }
        return jsonObject.getString("phoneNumber");
    }

    /**
     * @Description:充值额度选择
     * @Author: chenBing
     * @Date: 2022/10/8
     */
    @Override
    public UserRechargeFrontResponse getRechargeConfig() {
        UserRechargeFrontResponse userRechargeResponse = new UserRechargeFrontResponse();
        userRechargeResponse.setRechargeQuota(systemGroupDataService.getListByGid(SysGroupDataConstants.GROUP_DATA_ID_RECHARGE_LIST, UserRechargeItemResponse.class));
        String rechargeAttention = systemConfigService.getValueByKey(Constants.CONFIG_RECHARGE_ATTENTION);
        List<String> rechargeAttentionList = new ArrayList<>();
        if (StringUtils.isNotBlank(rechargeAttention)) {
            rechargeAttentionList = VcaUtil.stringToArrayStrRegex(rechargeAttention, "\n");
        }
        userRechargeResponse.setRechargeAttention(rechargeAttentionList);
        return userRechargeResponse;
    }

    /**
     * @param request
     * @Description:小程序支付
     * @Author: chenBing
     * @Date: 2022/10/8
     */
    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Error.class, VcaException.class})
    public OrderPayResultResponse recharge(UserRechargeRequest request) {
        request.setPayType(Constants.PAY_TYPE_WE_CHAT);

        //验证金额是否为最低金额
        String rechargeMinAmountStr = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_RECHARGE_MIN_AMOUNT);
        BigDecimal rechargeMinAmount = new BigDecimal(rechargeMinAmountStr);
        int compareResult = rechargeMinAmount.compareTo(request.getPrice());
        if (compareResult > 0) {
            throw new VcaException("充值金额不能低于" + rechargeMinAmountStr);
        }

        request.setGivePrice(BigDecimal.ZERO);

        if (request.getGroupDataId() > 0) {
            SystemGroupDataRechargeConfigVo systemGroupData = systemGroupDataService.getNormalInfo(request.getGroupDataId(), SystemGroupDataRechargeConfigVo.class);
            if (ObjectUtil.isNull(systemGroupData)) {
                throw new VcaException("您选择的充值方式已下架");
            }
            //售价和赠送
            request.setPrice(systemGroupData.getPrice());
            request.setGivePrice(systemGroupData.getGiveMoney());
        }
        User currentUser = userService.getInfoException();
        //生成系统订单
        UserRecharge userRecharge = new UserRecharge();
        userRecharge.setUid(currentUser.getUid());
        userRecharge.setOrderId(VcaUtil.getOrderNo("recharge"));
        userRecharge.setPrice(request.getPrice());
        userRecharge.setGivePrice(request.getGivePrice());
        userRecharge.setRechargeType(request.getFromType());
        boolean save = userRechargeService.save(userRecharge);
        if (!save) {
            throw new VcaException("生成充值订单失败!");
        }

        OrderPayResultResponse response = new OrderPayResultResponse();
        MyRecord record = new MyRecord();
        Map<String, String> unifiedorder = weChatPayService.unifiedRecharge(userRecharge, request.getClientIp());
        record.set("status", true);
        response.setStatus(true);
        WxPayJsResultVo vo = new WxPayJsResultVo();
        vo.setAppId(unifiedorder.get("appId"));
        vo.setNonceStr(unifiedorder.get("nonceStr"));
        vo.setPackages(unifiedorder.get("package"));
        vo.setSignType(unifiedorder.get("signType"));
        vo.setTimeStamp(unifiedorder.get("timeStamp"));
        vo.setPaySign(unifiedorder.get("paySign"));
        if (userRecharge.getRechargeType().equals(PayConstants.PAY_CHANNEL_WE_CHAT_H5)) {
            vo.setMwebUrl(unifiedorder.get("mweb_url"));
            response.setPayType(PayConstants.PAY_CHANNEL_WE_CHAT_H5);
        }
        response.setJsConfig(vo);
        ArrayList<String> orderids = new ArrayList<>();
        orderids.add(userRecharge.getOrderId());
        response.setOrderNos(orderids);
        return response;
    }
}
